<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="hrHUS" id="hrHUS"><span data-lake-id="ud60673d2" id="ud60673d2">典型回答</span></h1>
  <p data-lake-id="ufae12688" id="ufae12688"><br></p>
  <p data-lake-id="u28553078" id="u28553078"><span data-lake-id="u080b843e" id="u080b843e">在使用Maven进行项目构建的应用中，如果在应用运行期发生了NoSuchMethodError、ClassNotFoundException等异常或者错误时，需要考虑Jar包冲突的问题。</span></p>
  <p data-lake-id="u369d2c39" id="u369d2c39"><span data-lake-id="u081bc543" id="u081bc543">​</span><br></p>
  <p data-lake-id="u34cc5a95" id="u34cc5a95"><span data-lake-id="u4b01097e" id="u4b01097e">如果在应用中，我们同时依赖了两个第三方的jar包A，B，而A，B中又都依赖了C包，但是依赖的C的版本不同，那么就可能发生jar包冲突，导致最终编译打包后，应用的classpath中只有一个C的jar包。</span></p>
  <p data-lake-id="u27ee0f80" id="u27ee0f80"><span data-lake-id="ufc27c2d1" id="ufc27c2d1">​</span><br></p>
  <p data-lake-id="ub9e9b56d" id="ub9e9b56d"><span data-lake-id="u5649b2e3" id="u5649b2e3">那么，因为maven有一定的Jar包依赖传递原则，所以有可能他最终选择的jar包并不是我们真正想用的 ，那这时候怎么办呢？就需要我们手动的进行依赖仲裁，通过人工干预的方式来保留我们需要的jar包。</span></p>
  <p data-lake-id="uf1796f74" id="uf1796f74"><span data-lake-id="u1f141b1b" id="u1f141b1b">​</span><br></p>
  <h1 data-lake-id="ay9AL" id="ay9AL"><span data-lake-id="ude1a7544" id="ude1a7544">扩展知识</span></h1>
  <p data-lake-id="ue1d24ed3" id="ue1d24ed3"><br></p>
  <h2 data-lake-id="fQ5go" id="fQ5go"><span data-lake-id="u4cdfc596" id="u4cdfc596">依赖传递</span></h2>
  <p data-lake-id="u2d3783e8" id="u2d3783e8"><span data-lake-id="u176ac2ed" id="u176ac2ed" class="lake-fontsize-12" style="color: rgb(33, 37, 41)">几乎所有的Jar包冲突都和依赖传递原则有关，所以我们先说</span><span data-lake-id="ue16d0008" id="ue16d0008" class="lake-fontsize-12" style="color: rgb(214, 51, 132)">Maven</span><span data-lake-id="ud2065a08" id="ud2065a08" class="lake-fontsize-12" style="color: rgb(33, 37, 41)">中的依赖传递原则，主要由以下两个默认的原则：</span></p>
  <p data-lake-id="u531ad30b" id="u531ad30b"><span data-lake-id="u0dfc0409" id="u0dfc0409" class="lake-fontsize-12" style="color: rgb(33, 37, 41)">​</span><br></p>
  <p data-lake-id="u08e9e199" id="u08e9e199"><strong><span data-lake-id="u6b693f28" id="u6b693f28" class="lake-fontsize-12" style="color: rgb(33, 37, 41)">最短路径优先原则</span></strong></p>
  <p data-lake-id="uf6786a0b" id="uf6786a0b"><span data-lake-id="uba65682a" id="uba65682a" class="lake-fontsize-12" style="color: rgb(33, 37, 41)">假如引入了2个Jar包A和B，都传递依赖了Z这个Jar包：</span></p>
  <p data-lake-id="u15599aa0" id="u15599aa0"><span data-lake-id="u30d824a7" id="u30d824a7" class="lake-fontsize-12" style="color: rgb(33, 37, 41)">A -&gt; X -&gt; Y -&gt; Z(2.5)</span></p>
  <p data-lake-id="u27c70024" id="u27c70024"><span data-lake-id="u1f8a3cb5" id="u1f8a3cb5" class="lake-fontsize-12" style="color: rgb(33, 37, 41)">B -&gt; X -&gt; Z(2.0)</span></p>
  <p data-lake-id="u3c00fe67" id="u3c00fe67"><span data-lake-id="ucdd51a29" id="ucdd51a29" class="lake-fontsize-12" style="color: rgb(33, 37, 41)">那其实最终生效的是Z(2.0)这个版本。因为他的路径更加短。</span></p>
  <p data-lake-id="u6f8e5641" id="u6f8e5641"><span data-lake-id="u591e7699" id="u591e7699" class="lake-fontsize-12" style="color: rgb(33, 37, 41)">​</span><br></p>
  <p data-lake-id="ue1da6287" id="ue1da6287"><strong><span data-lake-id="u1d17a830" id="u1d17a830" class="lake-fontsize-12" style="color: rgb(33, 37, 41)">最先声明优先原则</span></strong></p>
  <p data-lake-id="uc4360505" id="uc4360505"><span data-lake-id="uf4276a21" id="uf4276a21" class="lake-fontsize-12" style="color: rgb(33, 37, 41)">如果路径长短一样，优先选最先声明的那个。</span></p>
  <p data-lake-id="u5a1b49f7" id="u5a1b49f7"><span data-lake-id="ubd6ec170" id="ubd6ec170" class="lake-fontsize-12" style="color: rgb(33, 37, 41)">A -&gt; Z(3.0)</span></p>
  <p data-lake-id="u0c15204c" id="u0c15204c"><span data-lake-id="u7f272091" id="u7f272091" class="lake-fontsize-12" style="color: rgb(33, 37, 41)">B -&gt; Z(2.5)</span></p>
  <p data-lake-id="u986cc583" id="u986cc583"><span data-lake-id="u4c1a8571" id="u4c1a8571" class="lake-fontsize-12" style="color: rgb(33, 37, 41)">这里A最先声明，所以传递过来的Z选择用3.0版本的。</span></p>
  <p data-lake-id="uf11dcb72" id="uf11dcb72"><span data-lake-id="u9007b734" id="u9007b734" class="lake-fontsize-12" style="color: rgb(33, 37, 41)">​</span><br></p>
  <p data-lake-id="u7b29c8d7" id="u7b29c8d7"><span data-lake-id="u608e2f0e" id="u608e2f0e" class="lake-fontsize-12" style="color: rgb(33, 37, 41)">显然这种默认的原则并不一定能使我们一定能获得自己真正想用的jar包，那么就需要人工仲裁了。</span></p>
  <h2 data-lake-id="SAwuL" id="SAwuL"><span data-lake-id="u6740a6b6" id="u6740a6b6">依赖树</span></h2>
  <p data-lake-id="u2b6c1703" id="u2b6c1703"><span data-lake-id="ub791c393" id="ub791c393" class="lake-fontsize-12">在开发中，我们可以通过maven的依赖树来判断是否发生了jar包冲突，以及如何发生的冲突，只需要在maven项目中使用以下命令即可打印依赖树：</span></p>
  <p data-lake-id="ua3f60743" id="ua3f60743"><span data-lake-id="u6e0bf89e" id="u6e0bf89e" class="lake-fontsize-12">​</span><br></p>
  <pre lang="java"><code>
mvn dependency:tree
</code></pre>
  <p data-lake-id="u760a9c4f" id="u760a9c4f"><br></p>
  <p data-lake-id="uef2a038e" id="uef2a038e"><br></p>
  <p data-lake-id="ue56e86aa" id="ue56e86aa"><span data-lake-id="u7f8876ac" id="u7f8876ac" class="lake-fontsize-12">打印出来的就是整个项目的jar包依赖的一个树形结构，我们找到发生了冲突的jar包就可以查看他的多种不同版本的依赖路径了。</span></p>
  <p data-lake-id="ude3feebd" id="ude3feebd"><span data-lake-id="udf2d4511" id="udf2d4511" class="lake-fontsize-12">​</span><br></p>
  <p data-lake-id="u54785082" id="u54785082"><span data-lake-id="u157031df" id="u157031df" class="lake-fontsize-12">在IDEA中，也可以使用maven helper插件来快速查看依赖树及冲突情况。</span></p>
  <p data-lake-id="u7adf5439" id="u7adf5439"><br></p>
  <h2 data-lake-id="jjwfo" id="jjwfo"><span data-lake-id="u1f5acf66" id="u1f5acf66">依赖仲裁</span></h2>
  <p data-lake-id="u91d7e6ea" id="u91d7e6ea"><br></p>
  <p data-lake-id="u742c82b3" id="u742c82b3"><span data-lake-id="ue7331fb3" id="ue7331fb3" class="lake-fontsize-12">如果经过分析，项目中发生了依赖冲突，那么就需要我们进行仲裁解决，解决的手段有以下几种：</span></p>
  <p data-lake-id="ufe198a8d" id="ufe198a8d"><span data-lake-id="uf19415cb" id="uf19415cb" class="lake-fontsize-12">​</span><br></p>
  <h3 data-lake-id="yC3Qo" id="yC3Qo"><span data-lake-id="u69ebd155" id="u69ebd155">排除依赖</span></h3>
  <p data-lake-id="ue902287b" id="ue902287b"><br></p>
  <p data-lake-id="ua62bcb6a" id="ua62bcb6a"><span data-lake-id="u669c1978" id="u669c1978" class="lake-fontsize-12">在pom文件中，我们可以在一</span><span data-lake-id="u3d6506eb" id="u3d6506eb">个&lt;dependency&gt;&lt;/dependency&gt;标签内使用排除jar包的方式进行jar包排除。</span></p>
  <p data-lake-id="u15a962d1" id="u15a962d1"><span data-lake-id="u399962a6" id="u399962a6">​</span><br></p>
  <p data-lake-id="u621bb237" id="u621bb237"><span data-lake-id="ucacf2766" id="ucacf2766">如我们想排除</span><span data-lake-id="u68fccffd" id="u68fccffd" class="lake-fontsize-12" style="color: rgb(33, 37, 41)">X中对于Y的依赖</span></p>
  <pre lang="java"><code>
&lt;dependency&gt;
	&lt;groupId&gt;com.hollis.javabagu&lt;/groupId&gt;
    &lt;artifactId&gt;X&lt;/artifactId&gt;
		&lt;exclusions&gt;
			&lt;exclusion&gt;
				&lt;groupId&gt;com.hollis.javabagu&lt;/groupId&gt;
				&lt;artifactId&gt;Y&lt;/artifactId&gt;
			&lt;/exclusion&gt;
	&lt;/exclusions&gt;
&lt;/dependency&gt;
</code></pre>
  <p data-lake-id="u2834a52f" id="u2834a52f"><br></p>
  <p data-lake-id="u8740fa2c" id="u8740fa2c"><span data-lake-id="u387b96ce" id="u387b96ce" class="lake-fontsize-12">这样，在项目中，就只会依赖X，而X依赖的Y的对应版本就会被排除了。</span></p>
  <p data-lake-id="u6f7cf6a8" id="u6f7cf6a8"><br></p>
  <h3 data-lake-id="T2QDa" id="T2QDa"><span data-lake-id="u9358d8d4" id="u9358d8d4">版本锁定</span></h3>
  <p data-lake-id="u8b9c696a" id="u8b9c696a"><br></p>
  <p data-lake-id="u730c9a8b" id="u730c9a8b"><span data-lake-id="u9b3b6614" id="u9b3b6614" class="lake-fontsize-12">一般在我们开发的web项目中都会有多个Module，每一个module都会有很多外部依赖，而多种依赖可能都依赖了jar包Y，就涉及到了多个版本，如果用排除法的话，需要一个一个的进行exclusion，这样太麻烦了。</span></p>
  <p data-lake-id="u26a35955" id="u26a35955"><br></p>
  <p data-lake-id="u8278bb59" id="u8278bb59"><span data-lake-id="u39cc08a0" id="u39cc08a0" class="lake-fontsize-12">有一个一劳永逸的方法进行版本锁定。</span></p>
  <p data-lake-id="u4ed3a6dd" id="u4ed3a6dd"><br></p>
  <p data-lake-id="ub85d629e" id="ub85d629e"><span data-lake-id="u5930e160" id="u5930e160">大型项目一般都会有父级pom，你想指定哪个版本只需要在你项目的父POM中定义如下：</span></p>
  <pre lang="java"><code>
&lt;dependencyManagement&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;com.hollis.javabagu&lt;/groupId&gt;
        &lt;artifactId&gt;Y&lt;/artifactId&gt;
        &lt;version&gt;3.0&lt;/version&gt;
    &lt;/dependency&gt;
&lt;/dependencyManagement&gt;
</code></pre>
  <p data-lake-id="u8e1bcf9f" id="u8e1bcf9f"><span data-lake-id="ua8a429be" id="ua8a429be">​</span><br></p>
  <p data-lake-id="u5f5a7ba9" id="u5f5a7ba9"><br></p>
  <p data-lake-id="u57cb8119" id="u57cb8119"><span data-lake-id="u2c87ba80" id="u2c87ba80">这样对于Y的jar包的依赖就只会保留3.0这个版本了。</span></p>
  <p data-lake-id="u31404a57" id="u31404a57"><span data-lake-id="u8b8654c4" id="u8b8654c4">​</span><br></p>
  <p data-lake-id="u49c7b58e" id="u49c7b58e"><span data-lake-id="u67e0eb43" id="u67e0eb43">但是需要注意的是dependencyManagement标签只能做依赖仲裁管理使用，他并不会引入对Jar包的依赖，还需要你使用dependency进行jar包引入。</span></p>
 </body>
</html>